﻿using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using Singer.Core;
using Singer.Middleware.Redis;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;

namespace Singer.Middleware.JwtAuth
{
    public class JwtAuthTokenManager : IJwtAuthTokenManager
    {
        public JwtOptions JwtAuthOptions => Options;
        private readonly JwtOptions Options;
        IRedisConnectionPool _redisConnectionPool;
        public JwtAuthTokenManager(IRedisConnectionPool redisConnectionPool, IOptions<JwtOptions> options)
        {
            _redisConnectionPool = redisConnectionPool;
            if (options.Value == null)
                throw new Exception("【JwtAuth】Config was not found.");
            options.Value.Validate();
            Options = options.Value;
        }

        public async Task<string> CreateTokenAsync(IEnumerable<Claim> claims)
        {
            //对称可逆加密 秘钥
            SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Options.SecurityKey));
            SigningCredentials sign = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            DateTime expirationTime = Cores.BeiJingNow.AddHours(Options.ExpirationHour);
            JwtSecurityToken securityToken = new JwtSecurityToken(
             issuer: Options.Issuer,
             audience: Options.Audience,
             claims: claims,
             expires: expirationTime,//token过期时间
             signingCredentials: sign);
            string token = new JwtSecurityTokenHandler().WriteToken(securityToken);
            //计算token的redisKey jwttokens:{MD5加密后的token}
            string tokenKey = JwtOptions.GetTokenCacheKey(token);
            //将token信息存redis
            IDatabase db = _redisConnectionPool.GetDatabase(JwtOptions.RedisDB);
            await db.StringSetAsync(tokenKey, token, TimeSpan.FromHours(Options.ExpirationHour));
            return token;
        }

        public async Task DeleteTokenAsync(string token)
        {
            string tokenKey = JwtOptions.GetTokenCacheKey(token);
            IDatabase db = _redisConnectionPool.GetDatabase(JwtOptions.RedisDB);
            await db.KeyDeleteAsync(tokenKey);
        }

        public Task<bool> ExistsTokenAsync(string token)
        {
            string tokenKey = JwtOptions.GetTokenCacheKey(token);
            IDatabase db = _redisConnectionPool.GetDatabase(JwtOptions.RedisDB);
            return db.KeyExistsAsync(tokenKey);
        }

        /// <summary>
        /// 读取JwtToken中的有效载荷
        /// </summary>
        public static ClaimsPrincipal? ReadToken(string token)
        {
            if (string.IsNullOrWhiteSpace(token))
                return null;
            JwtSecurityToken securityToken = new JwtSecurityToken(token);
            var claimsIdentity = new ClaimsIdentity(securityToken.Claims) ?? new ClaimsIdentity();
            var claimsPrincipal = new ClaimsPrincipal(claimsIdentity) ?? new ClaimsPrincipal();
            return claimsPrincipal;
        }
    }
}
